package com.lx.util.secure.algorithm.encrypt;


import com.lx.annotation.Note;
import com.lx.util.LX;
import com.lx.util.secure.algorithm.SecureAlgorithm;
import com.lx.util.secure.algorithm.SignAlgorithm;
import com.lx.util.secure.algorithm.coder.CoderType;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

@Slf4j
public class Rsa extends SignAlgorithm<Rsa> {

    public static Rsa publicKey(byte[] key){
        return new Rsa().key(key).pubKey(true);
    }

    public static Rsa publicKey(String keyStr){
        return new Rsa().key(keyStr).pubKey(true);
    }

    public static Rsa privateKey(byte[] key){
        return new Rsa().key(key).pubKey(false);
    }

    public static Rsa privateKey(String keyStr){
        return new Rsa().key(keyStr).pubKey(false);
    }

    @Override
    protected CoderType defaultKeyCoderType() {
        return CoderType.BASE64;
    }

    @Override
    protected CoderType defaultCoderType() {
        return CoderType.BASE64;
    }

    @Override
    protected String defaultEncryptAlgorithm() {
        return "RSA/ECB/PKCS1Padding";
    }


    @Override
    protected String defaultSignAlgorithm() {
        return "SHA256WithRSA";
    }

    @Override
    public byte[] sign(byte[] bytes) {
        try{
            Signature signature = Signature.getInstance(getSignAlgorithm());
            signature.initSign(loadPrivateKey(getKey()));
            signature.update(bytes);
            return signature.sign();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public boolean verify(byte[] bytes, byte[] sign) {
        try{
            Signature signature = Signature.getInstance(getSignAlgorithm());
            signature.initVerify(loadPublicKey(getKey()));
            signature.update(bytes);
            return signature.verify(sign);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public byte[] encrypt(byte[] bytes) {
        try{
            Cipher encryptCipher = Cipher.getInstance(getEncryptAlgorithm());
            Key key = loadKey(getKey(), true);
            encryptCipher.init(Cipher.ENCRYPT_MODE, key);
            return encryptCipher.doFinal(bytes);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public byte[] decrypt(byte[] bytes) {
        try{
            Cipher encryptCipher = Cipher.getInstance(getEncryptAlgorithm());
            Key key = loadKey(getKey(), false);
            encryptCipher.init(Cipher.DECRYPT_MODE, key);
            return encryptCipher.doFinal(bytes);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static final String KEY_ALGORITHM = "RSA";

    @Note("加载密钥")
    protected Key loadKey(byte[] keyBytes, boolean pubKey) throws Exception {
        if (getPubKey() != null){
            //指定了公钥还是私钥时按照指定处理 没有指定时按照方法默认推断
            pubKey = getPubKey();
        }
        return pubKey?loadPublicKey(keyBytes):loadPrivateKey(keyBytes);
    }

    public PublicKey loadPublicKey(byte[] publicKey) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
            return keyFactory.generatePublic(keySpec);
        }catch (Exception e){
            log.error("请检查公钥是否正确!",e);
            throw new RuntimeException(e);
        }

    }

    public PrivateKey loadPrivateKey(byte[] privateKey) {
        try {
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            return keyFactory.generatePrivate(keySpec);
        }catch (Exception e){
            log.error("请检查私钥是否正确!",e);
            throw new RuntimeException(e);
        }

    }

}